// thread header for threads
#ifndef _THREADS_THREAD
#define _THREADS_THREAD
#include <vector>
#include <functional>
#include <memory>
#include <Dinkum/threads/threads.h>
#include <Dinkum/threads/xthread>
#include <Dinkum/threads/mutex>
#include <Dinkum/threads/xtime>


namespace Dinkum
	{	// Dinkum Libraries
	namespace threads
		{	// Dinkum C++ Threads Library
 #if _HAS_TR1
 #else /* _HAS_TR1 */
struct _C_callable
	{	// C callable wrapper for callback with C++ linkage
	_C_callable(void *(*_Fp)(void *))
		: _Fn(_Fp)
		{	// construct with function pointer
		}

	void *(*_Fn)(void *);
	};

 #if _WIN32_C_LIB
extern "C" inline unsigned _STDCALL _C_call(void *_Dta)
	{	// call C++ callback
	return ((unsigned)((_C_callable *)_Dta)->_Fn(_Dta));
	}

 #elif _HAS_POSIX_C_LIB
extern "C" inline void *_C_call(void *_Dta)
	{	// call C++ callback
	return (((_C_callable *)_Dta)->_Fn(_Dta));
	}
 #endif /* library type */

template<class _Func>
	class _Binder
		: public _C_callable
	{	// bind a user-defined function to the thread start function
public:
	_Binder(_Func _Fp)
		: _Fn(_Fp), _C_callable(&_Binder<_Func>::_Callit)
		{	// construct
		}

	static void *_Callit(void *_Dta)
		{	// start the function from a newly created thread
		_Binder<_Func> _Bind = *(_Binder<_Func> *)_Dta;
		delete (_Binder<_Func>*)_Dta;

 #if _HAS_EXCEPTIONS
		try {	// don't let exceptions escape
			_Bind._Fn();
			}
		catch (...)
			{	// do nothing
			}

 #else /* _HAS_EXCEPTIONS */
		_Bind._Fn();
 #endif /* _HAS_EXCEPTIONS */

		return (0);
		}

private:
	_Func _Fn;
	};
 #endif /* _HAS_TR1 */

class thread
	{	// class for observing and managing threads
public:
	thread()
		: _Joinable(false)
		{	// construct
		_Thr = thrd_current();
		}

	template<class _Func>
		thread(_Func _Fp)
			: _Joinable(true)
		{	// construct with _Fp()
		_STD _Launch(&_Thr,
			_STD make_unique< _STD tuple<_Func> >(_Fp));
		}

	~thread() _NOEXCEPT
		{	// destroy
		if (_Joinable)
			thrd_detach(_Thr);
		}

	bool operator==(const thread& _Other) const
		{	// test for equality
		return (thrd_equal(_Thr, _Other._Thr) != 0);
		}

	bool operator!=(const thread& _Other) const
		{	// test for inequality
		return (!(*this == _Other));
		}

	void join()
		{	// join thread
		_THREAD_ASSERT(_Joinable,
			"threads::thread::join called on non-joinable thread");
		_Joinable = false;
		thrd_join(_Thr, 0);
		}

	static void sleep(const xtime& _Xt)
		{	// sleep for a time
		thrd_sleep(&_Xt);
		}

	static void yield()
		{	// yield control
		thrd_yield();
		}

private:
	thrd_t _Thr;
	bool _Joinable;
	friend class thread_group;

	thread(const thread&);	// not defined
	thread& operator=(const thread&);	// not defined
	};

class thread_group
	{	// class for observing multiple threads
public:
	thread_group()
		{	// construct
		}

	~thread_group() _NOEXCEPT
		{	// destroy all owned threads
		_STD vector<thread *>::iterator _Next = _Elts.begin();
		_STD vector<thread *>::iterator _End = _Elts.end();
		for (; _Next != _End; ++_Next)
			destroy(*_Next);
		}

	template<class _Func>
		thread *create_thread(_Func _Fp)
		{	// create a new thread
		thread *_Thr = 0;

 #if _HAS_EXCEPTIONS
		try {	// don't let exceptions escape
			_Thr = new thread(_Fp);
			add_thread(_Thr);
			}
		catch (...)
			{	// delete incomplete thread
			delete _Thr;
			throw;
			}

 #else /* _HAS_EXCEPTIONS */
		_Thr = new thread(_Fp);
		add_thread(_Thr);
 #endif /* _HAS_EXCEPTIONS */

		return (_Thr);
		}

	void add_thread(thread *_Thrd)
		{	// add a thread
		_THREAD_ASSERT(_Thrd->_Joinable,
			"threads::thread_group::add_thread called "
				"with non-joinable thread");
		mutex::scoped_lock lock(_Mtx);
		if (_STD find(_Elts.begin(), _Elts.end(), _Thrd) == _Elts.end())
			_Elts.push_back(_Thrd);
		}

	void remove_thread(thread *_Thrd)
		{	// remove a thread
		mutex::scoped_lock lock(_Mtx);
		_Elts.erase(_STD find(_Elts.begin(), _Elts.end(), _Thrd));
		}

	void join_all()
		{	// join all threads
		mutex::scoped_lock lock(_Mtx);
		_STD vector<thread *>::iterator _Next = _Elts.begin();
		_STD vector<thread *>::iterator _End = _Elts.end();
		for (; _Next != _End; ++_Next)
			join(*_Next);
		}

private:
	mutex _Mtx;
	_STD vector<thread *> _Elts;

	thread_group(const thread_group&);	// not defined
	thread_group& operator=(const thread_group&);	// not defined

	static void destroy(thread *_Thrd)
		{	// delete a pointer to thread
		delete _Thrd;
		}

	static inline void join(thread *_Thrd)
		{	// join a thread
		_Thrd->join();
		}
	};
		}	// namespace threads
	}	// namespace Dinkum
#endif /* _THREADS_THREAD */

/*
 * This file is derived from software bearing the following
 * restrictions:
 *
 * (c) Copyright William E. Kempf 2001
 *
 * Permission to use, copy, modify, distribute and sell this
 * software and its documentation for any purpose is hereby
 * granted without fee, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation. William E. Kempf makes no representations
 * about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V6.50:1422 */
